﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.Build.Client;

namespace UCL.Powersource.TFS
{
    public class RemoteBuilderQueueItem : IRemoteBuilderQueueItem
    {
        public ITfsTeamProjectConfiguration Project { get; private set; }
        public ITimeFrame TimeFrame { get; private set; }
        public IBuildDetail BuildDetail { get; private set; }
        public QueuedItemStatus Status { get; internal set; }
        public Exception BuildException { get; private set; }
        public Boolean HasTerminatedNormally { get; private set; }
        public bool ForceRecalculate { get; private set; }
        public IRemoteBuilderQueue Owner { get { return _Owner; } }
        private RemoteBuilderQueue _Owner;

        //public event EventHandler<QueuedItemChangedEventArgs> Changed;
        private event EventHandler _Completed;
        public event EventHandler Completed
        {
            add
            {
                _Completed += value;
                RemoveOnCompleted.Add(value);
            }
            remove
            {
                _Completed -= value;
            }
        }
        internal readonly List<EventHandler> RemoveOnCompleted = new List<EventHandler>();
        

        internal RemoteBuilderQueueItem(ITfsTeamProjectConfiguration pr, ITimeFrame tf, RemoteBuilderQueue owner,bool forceRecalculate)
        {
            Project = pr;
            TimeFrame = tf;
            BuildDetail = null;
            Status = QueuedItemStatus.New;
            ForceRecalculate = forceRecalculate;
            _Owner = owner;
        }

        ILog _Log;
        ILog Log
        {
            get { return _Log ?? (_Log = LogManager.GetLog(this.GetType())); }
        }

        internal void Finish(IBuildDetail buildDetail, Exception exc, bool EnumeratorNormallyFinished)
        {
            if (Status == QueuedItemStatus.Finished)
            {
                Log.DebugMethod("Ignoring request to finish, because RemoteBuildQueueItem is already marked as finished: {0} .", this.ToString());
                return;
            }
            lock (_Owner)
            {
                BuildException = exc;
                this.HasTerminatedNormally = EnumeratorNormallyFinished;
                if (buildDetail != null)
                {
                    if (buildDetail.TeamProject != Project.UniqueName)
                        throw new InvalidOperationException(string.Format("Cannot finish build for project {0} with bd detail for project {1}", Project.UniqueName, buildDetail.TeamProject));
                }
                else
                {
                    // No need anymore -we allowed skipped builds
                    //throw new ArgumentNullException("buildDetail", "Cannot finish bd with null bd definition");
                }
                
                if (Status != QueuedItemStatus.Activated)
                    throw new InvalidOperationException(string.Format("Cannot finish build for project {0} if its status is {1} and not {2}", Project.UniqueName, Status, QueuedItemStatus.Activated.ToString()));

                BuildDetail = buildDetail;

                Status = QueuedItemStatus.Finished;
            }

            RaiseChange(QueuedItemStatus.Finished);
        }

        internal string GetActionString()
        {
            string bdString = "";
            string team = "#NULL";
            string tf = "#NULL";
            if (this.BuildDetail != null && this.BuildDetail.BuildDefinition != null && this.BuildDetail.BuildDefinition.Name!=null)
            {
                bdString = string.Format("[BuildDefinition on server: {0}]", this.BuildDetail.BuildDefinition.Name);
            }
            if (this.Project != null) team = this.Project.Label;
            if (this.TimeFrame != null) tf = this.TimeFrame.Label;
            return string.Format("Build {1} at {2} - {3} {0}", bdString, team, tf, this.Status.ToString());
        }

        private void RaiseChange(QueuedItemStatus newStatus)
        {
            //if (Changed != null) Changed(this, new QueuedItemChangedEventArgs(newStatus));
            if (newStatus == QueuedItemStatus.Finished && _Completed != null)
            {
                _Completed(this, EventArgs.Empty);
                //Remove events registered for "called once" only
                foreach (var r in RemoveOnCompleted)
                {
                    Completed -= r;
                }
                RemoveOnCompleted.Clear();
            }
        }

        internal void Enqueued()
        {
            lock (_Owner)
            {
                if (this.Status != QueuedItemStatus.New) throw new InvalidOperationException(String.Format("An attempt to change status for {0} to Enqueued failed. Current status is {1}, but it must be {2}", this.ToString(), this, Status, QueuedItemStatus.New));
                this.Status = QueuedItemStatus.Enqueued;
            }
            RaiseChange(QueuedItemStatus.Enqueued);
        }

        internal void Activated()
        {
            lock (_Owner)
            {
                if (this.Status != QueuedItemStatus.Enqueued) throw new InvalidOperationException(String.Format("An attempt to change status for {0} to Activated failed. Current status is {1}, but it must be {2}", this.ToString(), this, Status, QueuedItemStatus.Enqueued));
                this.Status = QueuedItemStatus.Activated;
            }
            RaiseChange(QueuedItemStatus.Activated);
        }
        public override string ToString()
        {
            var bdString = "#NULL";
            if (BuildDetail != null)
                bdString = string.Format("BuildDetail: {0}, {1} - {2}", BuildDetail.Status, BuildDetail.StartTime, BuildDetail.FinishTime);
            if (this.Project != null && this.TimeFrame != null)
            {
                return string.Format("{0} [{1},{2}][{3},Recalc:{4}] - {5}", this.GetType().Name, this.Project.Label, this.TimeFrame.Label, this.Status, this.ForceRecalculate,bdString);
            }
            else
                return string.Format("RemoteBuild {0} [Unitilized][{1},Recalc:{2}] - {3}", this.GetType().Name, this.Status, this.ForceRecalculate, bdString);
        }


    }

}
